import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import router, { resetRouter, constantRoutes, asyncRoutes } from '@/router'

/**
 * 加载路由模块对象
 * @param view
 */
export const loadView = (view) => { // 路由懒加载
  // return () => import(`../../views/${view}`)  // 这是一种错误的写法 ，动态路由不能直接使用import
  // return (resolve) => require([`@/views/${view}`], resolve) // 这也是一种错误的写法，不能使用自定义的路径 @
// -----------------------------以下全是正确的写法----------------------------------------------
  return (resolve) => require([`../../views/${view}`], resolve)
  // return require(`../../views/${view}`).default
  // return () => Promise.resolve(require(`../../views/${view}`).default)
}

/**
 * tree遍历，过滤出 router中path集合
 * @param tree
 * @param ope
 */
const connectLink = (tree, ope) => {
  const walk = (tree) => {
    const queue = []
    ope(tree)
    if (tree.children) {
      queue.push({ nodes: tree.children })
    }
    while (queue.length) {
      const item = queue.pop()
      item.nodes && item.nodes.forEach(node => {
        ope(node)
        if (node.children) {
          queue.push({ nodes: node.children })
        }
      })
    }
  }
  walk(tree)
}

/**
 * 过滤出 routers中符合条件的routers
 * @param tree 树形数据
 * @param ope  对树节点的操作
 * @param filter 过滤函数
 * @returns {*|{}}
 */
export const treeFilter = (tree, ope, filter) => {
  const walkAndCopy = (tree) => {
    const queue = []
    if (filter(tree)) {
      let copy = {}
      ope(tree)
      // copy.name = tree.name
      copy = { ...tree }
      if (tree.children) {
        copy.children = []
        queue.push({
          nodes: tree.children,
          copyNodes: copy.children
        })
      }
      // handle(tree, queue, depth)
      while (queue.length) {
        const item = queue.pop()
        item.nodes && item.nodes.forEach(node => {
          if (filter(node)) {
            let copyNode = {}
            ope(node)
            // copyNode.name = node.name
            copyNode = { ...node }
            if (node.children) {
              copyNode.children = []
              queue.push({
                nodes: node.children,
                copyNodes: copyNode.children
              })
            }
            item.copyNodes.push(copyNode)
          }
        })
      }
      return copy
    }
  }
  return walkAndCopy(tree)
}
/**
 * 数组 转 tree
 * @param menus 数组
 * @param roles 附加属性
 * @returns {[]}
 */
const menus2Routers = (menus, roles) => {
  menus = menus.filter(menu => menu.resourceCode.indexOf(':') === -1)
  menus = menus.map(menu => {
    return {
      path: menu.resourceLink,
      name: menu.resourceCode,
      component: menu.resourceLink,
      meta: {
        title: menu.resourceName,
        icon: menu.resourceIcon ? menu.resourceIcon : '',
        router_id: menu.resourceId,
        parent_id: menu.parentId,
        roles
      }
    }
  })
  // () => import('@/layout')
  const newList = []
  for (let i = 0; i < menus.length; i++) {
    if (menus[i].meta.parent_id === 0) {
      menus[i].component = () => import('@/layout')
      newList.push(menus[i])
    } else {
      const findItem = menus.find((item) => item.meta.router_id === menus[i].meta.parent_id)
      if (findItem) {
        menus[i].path = '' + findItem.path + menus[i].path
        menus[i].component = loadView(findItem.name + '/' + menus[i].name)
        if (findItem.children) {
          // 更改原数组就相当于给新数组里面添加了children，因为新数组里面元素的地址和原数组是一个、
          findItem.children.push(menus[i])
        } else {
          findItem.children = [menus[i]]
        }
      }
    }
  }
  return newList
}

const state = {
  token: getToken(),
  user: {},
  name: '',
  avatar: '',
  introduction: '',
  roles: [],
  auths: [],
  menus: [],
  routes: [],
  addRoutes: []
}

const mutations = {
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_USER: (state, user) => {
    state.user = user
  },
  SET_NAME: (state, name) => {
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  },
  SET_INTRODUCTION: (state, introduction) => {
    state.introduction = introduction
  },
  SET_ROLES: (state, roles) => {
    state.roles = roles
  },
  SET_AUTHS: (state, auths) => {
    state.auths = auths
  },
  SET_MENUS: (state, menus) => {
    state.menus = menus
  },
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  // user login
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login({ username: username.trim(), password: password })
        .then(response => {
          const { data, code } = response
          if (code === 200) {
            commit('SET_TOKEN', data)
            setToken(data)
            resolve()
          } else {
            router.push('/')
          }
        })
        .catch(error => {
          reject(error)
        })
    })
  },

  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token)
        .then(response => {
          const user = response.data
          const username = user.username
          const roles = user.roles
          const roleCodes = roles.map(role => role.roleCode)
          const menus = []
          roles.forEach(role => {
            const resources = role.resources
            resources.forEach(r => menus.push(r))
          })
           // 排序
          menus.sort((a, b) => {
            if (a.resourceSort > b.resourceSort) { // a b 分别是Arr中的 56 21
              return 1 // 返回正数 ，b排列在a之前
            } else {
              return -1 // 返回负数 ，a排列在b之前
            }
          })

          // ------------ 第一种  直接由数据库数据生成动态路由
          const dynamicRoutes = menus2Routers(menus, roleCodes)
          console.log('-------dynamicRoutes------', dynamicRoutes)
          // ------------ 第一种 end ----------------------------------------------------
          // ------------ 第二种  前端配置全路由，然后根据数据库url进行过滤，即treeFilter --------------------------
          // 获得权限url
          const auths = []
          dynamicRoutes.forEach(dr => {
            connectLink(dr, node => auths.push(node.path))
          })
          // 过滤出正确的url
          const authRouters = []
          asyncRoutes.forEach(ar => {
            const n = treeFilter(ar, () => {}, node => auths.indexOf(node.path) !== -1)
            if (n) {
              authRouters.push(n)
            }
          })
          console.log('-------authRouters------', authRouters)
          // ------------ 第二种 end ----------------------------------------------------
          commit('SET_USER', user)
          commit('SET_NAME', username)
          commit('SET_AVATAR', 'AVATAR')
          commit('SET_INTRODUCTION', 'INTRODUCTION')
          commit('SET_ROLES', roleCodes)
          commit('SET_AUTHS', auths)
          commit('SET_MENUS', menus)
          commit('SET_ROUTES', dynamicRoutes)

          resolve({ user, roleCodes, auths, menus, dynamicRoutes: dynamicRoutes })
        })
        .catch(error => {
          reject(error)
        })
    })
  },
  // user logout
  logout({ commit, state, dispatch }) {
    return new Promise((resolve, reject) => {
      logout(state.token).then(() => {
        commit('SET_TOKEN', '')
        commit('SET_ROLES', [])
        removeToken()
        resetRouter()

        // reset visited views and cached views
        // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
        dispatch('tagsView/delAllViews', null, { root: true })

        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

  // remove token
  resetToken({ commit }) {
    return new Promise(resolve => {
      commit('SET_TOKEN', '')
      commit('SET_ROLES', [])
      removeToken()
      resolve()
    })
  },

  // dynamically modify permissions
  async changeRoles({ commit, dispatch }, role) {
    const token = role + '-token'

    commit('SET_TOKEN', token)
    setToken(token)

    const { roles } = await dispatch('getInfo')

    resetRouter()

    // generate accessible routes map based on roles
    const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
    // dynamically add accessible routes
    router.addRoutes(accessRoutes)

    // reset visited views and cached views
    dispatch('tagsView/delAllViews', null, { root: true })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}
